package br.com.lambdacore.persistence.dao.jpa;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.Query;

import br.com.lambdacore.domain.model.EntityModel;
import br.com.lambdacore.persistence.dao.SqlDAO;
import br.com.lambdacore.persistence.dao.helper.PersistentClassDiscovery;
import br.com.lambdacore.validator.Validator;

public abstract class JpaDAOImpl<T extends EntityModel> implements SqlDAO<T> {


	public boolean contains(T entity) {
		return this.getEntityManager().contains(entity);
	}

	public T find(int id) {
		return this.getEntityManager().find(this.getPersistentClass(), id);
	}

	@Override
	public Collection<? extends T> find(String query) {
		return this.find(query, null);
	}

	@SuppressWarnings("unchecked")
	public Collection<? extends T> find(String query,
			Map<String, Object> parameters) {
		try {
			return this.setParametersToQuery(query, parameters).getResultList();
		} catch (Exception e) {
			return null;
		}
	}

	@Override
	public Collection<? extends T> findFirstResult(String query,
			Integer firstResult) {
		return this.findFirstResult(query, null, firstResult);
	}

	@SuppressWarnings("unchecked")
	public Collection<? extends T> findFirstResult(String query,
			Map<String, Object> parameters, Integer firstResult) {
		try {
			return this.setParametersToQuery(query, parameters).setFirstResult(
					firstResult).getResultList();
		} catch (Exception e) {
			return null;
		}
	}

	@Override
	public Collection<? extends T> findMaxResults(String query,
			Integer maxResults) {
		return this.findMaxResults(query, null, maxResults);
	}

	@SuppressWarnings("unchecked")
	public Collection<? extends T> findMaxResults(String query,
			Map<String, Object> parameters, Integer maxResults) {
		try {
			return this.setParametersToQuery(query, parameters).setMaxResults(
					maxResults).getResultList();
		} catch (Exception e) {
			return null;
		}
	}

	@Override
	public Collection<? extends T> findRange(String query, Integer firstResult,
			Integer maxResults) {
		return this.findRange(query, null, firstResult, maxResults);
	}

	@SuppressWarnings("unchecked")
	public Collection<? extends T> findRange(String query,
			Map<String, Object> parameters, Integer firstResult,
			Integer maxResults) {
		try {
			return this.setParametersToQuery(query, parameters).setFirstResult(
					firstResult).setMaxResults(maxResults).getResultList();
		} catch (Exception e) {
			return null;
		}
	}

	@Override
	public T findSingleResult(String query) {
		return this.findSingleResult(query, null);
	}

	@SuppressWarnings("unchecked")
	public T findSingleResult(String query, Map<String, Object> parameters) {
		try {
			return (T) this.setParametersToQuery(query, parameters)
					.getSingleResult();
		} catch (Exception e) {
			return null;
		}
	}

	public T getReference(T entity) {
		return this.getEntityManager().getReference(this.getPersistentClass(),
				entity);
	}

	public void lock(T entity, LockModeType lockModeType) {
		this.getEntityManager().lock(entity, lockModeType);
	}

	public void lockRead(T entity) {
		this.lock(entity, LockModeType.READ);
	}

	public void lockWrite(T entity) {
		this.lock(entity, LockModeType.WRITE);
	}

	public T merge(T entity) {
		return this.getEntityManager().merge(entity);
	}

	public void persist(T entity) {
		this.getEntityManager().persist(entity);
	}

	public void refresh(T entity) {
		this.getEntityManager().refresh(entity);
	}

	public void remove(T entity) {
		this.getEntityManager().remove(entity);
	}

	protected abstract EntityManager getEntityManager();

	@SuppressWarnings({"unchecked"})
	protected Class<T> getPersistentClass() {
		return PersistentClassDiscovery.<T>newInstance(this).getPersistentClass();
	}

	private Query createNamedQuery(String query) {
		return this.getEntityManager().createNamedQuery(query);
	}

	private Query setParametersToQuery(String query, Map<String, ?> parameters) {
		Query namedQuery = this.createNamedQuery(query);
		if (Validator.is(parameters).notNullOrEmpty()) {
			for (Entry<String, ?> entry : parameters.entrySet()) {
				namedQuery.setParameter(entry.getKey(), entry.getValue());
			}
		}
		return namedQuery;
	}

}
